package com.globallogic.javatrainings.sergiiivanov.model;

import java.io.Serializable;

import com.globallogic.javatrainings.sergiiivanov.model.Lecture.LectureType;
import com.globallogic.javatrainings.sergiiivanov.resources.ErrorCodes;
import com.globallogic.javatrainings.sergiiivanov.resources.Messages;

/**
 * Stores information about rooms<br>
 * Contains list of appointed lectures
 * 
 * @author Sergey
 * 
 */
public class Room implements Serializable, MyArrayToNamesCovertable {
    /**
     * Type of the room
     * 
     * @author Sergey
     * 
     */
    public enum RoomType {
        /**
         * Auditorium is applicable for lectures, examinations and test lessons
         */
        AUDITORIUM(Messages.ROOM_AUDITORIUM),
        /**
         * Laboratory is applicable for practical and test lessons
         */
        LABORATORY(Messages.ROOM_LABORATORY),
        /**
         * Other rooms are not used for lessons
         */
        OTHER(Messages.ROOM_OTHER);
        private final String representation;

        /**
         * Returns all values in a list of names as String[]
         * 
         * @return String[] - list of {@link #getRepresentation()}
         */
        public static String[] getNamesList() {
            String[] res = new String[values().length];
            for (int i = 0; i < values().length; i++) {
                res[i] = values()[i].getRepresentation();
            }
            return res;
        }

        RoomType(String shownAs) {
            this.representation = shownAs;
        }

        /**
         * Returns String representation of the type
         * 
         * @return name of the type
         */
        public String getRepresentation() {
            return representation;
        }
    }

    private static final long serialVersionUID = 1L;
    private int capacity = 0;
    private Personnel responsiblePerson;
    private String roomName;
    private RoomType roomType = RoomType.OTHER;
    private Building building;
    private MyArray<Lecture> appointedLections;

    /**
     * Default constructor
     */
    public Room() {
        appointedLections = new MyArray<Lecture>();
    }

    /**
     * Sets building that room belongs to.
     * 
     * @param b
     *            - valid name (not null, not empty, unique within building)
     * @throws UniversityWarning
     *             if name is not valid
     */
    protected void setBuilding(Building b) throws UniversityWarning {
        if (b != null && !b.equals(building)) {
            b.hasDuplicates(roomName);
            building = b;
        }
    }

    /**
     * Quick constructor. Sets name.
     * 
     * @param newName
     *            not null, not empty
     * @throws UniversityWarning
     *             name in null or empty
     */
    public Room(String newName) throws UniversityWarning {
        setName(newName);
        appointedLections = new MyArray<Lecture>();
    }

    /**
     * @return capacity value set by {@link #setCapacity(int)}
     */
    public int getCapacity() {
        return capacity;
    }

    /**
     * Adds new entry into the list of appointed lectures. Called from
     * {@link Lecture#setRoom(Room)}
     * 
     * @param l
     */
    protected void addLecture(Lecture l) {
        if (appointedLections.indexOf(l) < 0)
            appointedLections.add(l);
    }

    /**
     * Removes link to provided lecture from the list
     * 
     * @param lec lecture to unlink
     */
    protected void removeLecture(Lecture lec) {
        appointedLections.remove(lec);
    }

    /**
     * Returns name of room type null-safe
     * 
     * @return {@link RoomType#getRepresentation()} or
     *         {@link Const#STRING_EMPTY}
     */
    public String getTypePrint() {
        return roomType == null ? Const.STRING_EMPTY : roomType.getRepresentation();
    }

    /**
     * Returns name as it was set by {@link #setName(String)}
     */
    @Override
    public String getName() {
        return roomName;
    }

    /**
     * Returns responsible personnel set by
     * {@link #setResponsiblePerson(Personnel)}
     * 
     * @return {@link Personnel} or <b>null</b>
     */
    public Personnel getResponsiblePerson() {
        return responsiblePerson;
    }

    /**
     * @return <b>true</b> when room type is {@link RoomType#AUDITORIUM}
     */
    public boolean isAuditorium() {
        return (roomType == RoomType.AUDITORIUM);
    }

    /**
     * @return <b>true</b> when room type is {@link RoomType#LABORATORY}
     */
    public boolean isLaboratory() {
        return (roomType == RoomType.LABORATORY);
    }

    /**
     * Sets room type to {@link RoomType#AUDITORIUM}
     * 
     * @throws UniversityWarning
     *             if appointed lecture is not relevant for auditory
     */
    public void setToAuditorium() throws UniversityWarning {
        for (Lecture l : appointedLections) {
            if (l.getLectureType().equals(LectureType.PRACTICE)) {
                throw new UniversityWarning(ErrorCodes.ROOM_HAS_LINKS);
            }
        }
        roomType = RoomType.AUDITORIUM;
    }

    /**
     * Sets room type to {@link RoomType#LABORATORY}
     * 
     * @throws UniversityWarning
     *             if appointed lecture is not relevant for laboratory
     */
    public void setToLaboratory() throws UniversityWarning {
        for (Lecture l : appointedLections) {
            if (l.getLectureType().equals(LectureType.LECTURE)) {
                throw new UniversityWarning(ErrorCodes.ROOM_HAS_LINKS);
            }
        }
        roomType = RoomType.LABORATORY;
    }

    /**
     * Sets room type to {@link RoomType#OTHER}
     * 
     * @throws UniversityWarning
     *             when have any appointed lectures
     */
    public void setToOther() throws UniversityWarning {
        if (appointedLections.size() > 0) {
            throw new UniversityWarning(ErrorCodes.ROOM_HAS_LINKS);
        }
        roomType = RoomType.OTHER;
    }

    /**
     * Updates capacity
     * 
     * @param newCapacity
     *            - valid capacity (>0)
     * @throws UniversityWarning
     *             when appointed Lecture does not fit new capacity
     */
    public void setCapacity(int newCapacity) throws UniversityWarning {
        if (newCapacity >= 0) {
            for (Lecture l : appointedLections) {
                if (l.availablePlaces() < (capacity - newCapacity)) {
                    throw new UniversityWarning(ErrorCodes.ROOM_IS_TOO_SMALL);
                }
                ;
            }
            capacity = newCapacity;
        } else {
            throw new UniversityWarning(ErrorCodes.ROOM_NEGATIVE_CAPACITY);
        }
    }

    /**
     * Sets responsible personnel and updates links
     * 
     * @param responcible
     *            {@link Personnel}
     */
    public void setResponsiblePerson(Personnel responcible) {
        if (responcible != null) {
            if (!responcible.equals(this.responsiblePerson) && responsiblePerson != null)
                this.responsiblePerson.removeRoom(this);
            responcible.addRoom(this);
        }
        this.responsiblePerson = responcible;
    }

    /**
     * Updates name if valid new name provided
     * 
     * @param newRoomName
     *            - valid name (not null, not empty, unique in the building)
     * @throws UniversityWarning
     *             name is not valid
     */
    public void setName(String newRoomName) throws UniversityWarning {
        if (newRoomName == null || newRoomName == Const.STRING_EMPTY) {
            throw new UniversityWarning(ErrorCodes.ROOM_NAME_EMPTY);
        }
        if (!newRoomName.equals(getName())) {
            validateRoomName(newRoomName);
            roomName = newRoomName;
        }
    }

    /**
     * @param newName
     *            name to check
     * @throws UniversityWarning
     *             when equal name found in the building set by
     *             {@link #setBuilding(Building)}
     */
    private void validateRoomName(String newName) throws UniversityWarning {
        if (building != null)
            building.hasDuplicates(newName);
    }

    /**
     * Removes all links to the room. Used before removing room
     * 
     * @throws UniversityWarning
     *             should never happen until {@link Lecture#setRoom(Room)} is
     *             not broken
     */
    public void removeLinks() throws UniversityWarning {
        for (Lecture l : appointedLections.copy()) {
            l.setRoom(null);
        }
        appointedLections.clear();
        if (responsiblePerson != null) {
            responsiblePerson.removeRoom(this);
        }
        responsiblePerson = null;
        building = null;
    }

    /**
     * Returns copy of linked lectures
     * 
     * @return {@link MyArray} of {@link Lecture}
     */
    public MyArray<Lecture> getLinkedLectures() {
        return appointedLections.copy();
    }

}
